package sh01

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"gopkg.in/mgo.v2/bson"

	"gitee.com/Flearning/go_crawl/modules/mgobson"

	"gopkg.in/mgo.v2"
)

var (
	CrawlUri            string
	MgoXueqiuCollection string
	MgoWSet             bool
	Req                 *ReqCrawlInfo
)

type (
	CrawlInfo struct {
		UriPath string
		Page    int
		Sort    string
	}
	ReqCrawlInfo struct {
		Method        string            `json:"method,omitempty"`
		Url           string            `json:"url,omitempty"`
		MgoCollection string            `json:"mgoCollection,omitempty"`
		Header        map[string]string `json:"req_header,omitempty"`
		Prames        []CrawlPrames     `json:"prames,omitempty"`
	}
	CrawlPrames struct {
		Symbol string `json:"symbol,omitempty"`
		Page   int    `json:"page,omitempty"`
		Sort   string `json:"sort,omitempty"`
		Sleep  int64  `json:"sleep,omitempty"`
	}
)

func NewCrawlInfo(ci *CrawlInfo) *CrawlInfo {
	return &CrawlInfo{
		UriPath: CrawlUri,
		Page:    ci.Page,
		Sort:    ci.Sort,
	}
}

func (c *CrawlInfo) CrawlStatuses(page int, sort string) (int, []byte) {
	if page == 0 {
		page = c.Page
	}
	if len(sort) == 0 || sort == "" {
		sort = c.Sort
	}
	uriPath := fmt.Sprintf(c.UriPath, page, sort)
	return httpNewRequest(uriPath)
}

func httpNewRequest(uriPath string) (statusCode int, respBody []byte) {
	var err error
	tr := &http.Transport{
		MaxIdleConns:       10,
		IdleConnTimeout:    30 * time.Second,
		DisableCompression: true,
		TLSClientConfig:    &tls.Config{InsecureSkipVerify: true},
	}

	client := &http.Client{
		Transport: tr,
	}

	req, _ := http.NewRequest(Req.Method, uriPath, nil)
	req.Header.Set("User-Agent", Req.Header["UserAgent"])
	req.Header.Add("Cookie", Req.Header["Cookie"])
	req.Header.Add("Host", Req.Header["Host"])
	req.Header.Add("Referer", Req.Header["Referer"])
	req.Header.Add("HttpsSafeCode", Req.Header["HttpsSafeCode"])
	req.Header.Add("X-Requested-With", Req.Header["X-Requested-With"])
	req.Header.Add("Connection", Req.Header["Connection"])

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("httpNewRequest error: ", err)
	}
	statusCode = resp.StatusCode
	if statusCode == http.StatusOK {
		respBody, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("httpNewRequest ReadAll error: ", err)
		}
		return
	}
	return statusCode, nil
}

func (r *BodyJsonFormat) BsonData(rb []byte) []interface{} {
	rbf := new(BodyJsonFormat)
	json.Unmarshal(rb, rbf)
	bsonD := make([]ListStruct, 0)
	json.Unmarshal(*rbf.List, &bsonD)
	bsonI := make([]interface{}, 0)
	for _, val := range bsonD {
		bsonI = append(bsonI, val)
	}
	return bsonI
}

func RunInstert(v ...interface{}) error {
	query := func(c *mgo.Collection) error {
		return c.Insert(v...)
	}
	return mgobson.WatchCollection(MgoXueqiuCollection, query)
}

func RunCount() (int, error) {
	var count int
	var err error
	query := func(c *mgo.Collection) error {
		count, err = c.Count()
		return err
	}
	err = mgobson.WatchCollection(MgoXueqiuCollection, query)
	return count, err
}

func (c *CrawlInfo) RunCrawl() (maxPage int, nextPage int, statusCode int) {
	var (
		respBodyBtye []byte
	)
	statusCode, respBodyBtye = c.CrawlStatuses(c.Page, c.Sort)
	fmt.Println("this crawl data statusCode: ", statusCode)
	if statusCode != http.StatusOK {
		fmt.Println("this crawl data statusCode Error: ", statusCode)
		panic("statusCode Error crawl exit.")
	}
	j2s := new(BodyJsonFormat)
	json.Unmarshal(respBodyBtye, j2s)

	bsonD := j2s.BsonData(respBodyBtye)

	if MgoWSet {
		RunInstert(bsonD...)
		fmt.Println("this crawl data length: ", len(bsonD))
	}
	maxPage = j2s.MaxPage
	nextPage = c.Page + 1
	c.Page = nextPage
	return
}

func ReadCrawlSiteInfo(rb []byte) *ReqCrawlInfo {
	reqInfo := new(ReqCrawlInfo)
	json.Unmarshal(rb, reqInfo)
	return reqInfo
}

func checkData(b *ListStruct) bool {
	var check bool
	c := mgobson.MgoSession.DB(mgobson.MgoDialInfo.Database).C(MgoXueqiuCollection)
	query := bson.M{
		"created_at": b.CreatedAt,
	}
	n, _ := c.Find(query).Count()
	if n > 0 {
		check = false
	} else {
		check = true
	}
	return check
}
